#include "Bagging.h"


void LcBagging::save( string filename_prefix )
{
	string temp = filename_prefix + "_bag_" + my_reader->video_name + "_" + feature_code_string ;

	cout << temp << endl;

	{//write the main file
		string filename = temp + "_" + "main.txt" ;
		FILE * fin = fopen( filename.c_str() ,"w");
		fprintf(fin,"%d\t%d\t%d\t%d\n", n_classifier, my_reader->f_start, my_reader->f_end, my_reader->f_rate);
		fclose( fin );
	}

	
	for(int i = 0 ; i< n_classifier;i++)
	{
		stringstream ss; ss << i;
		string sub_name = temp + "_" + ss.str();
		h[i]->save( sub_name.c_str() );
	}
}

void LcBagging::load( string filename_prefix )
{
	string temp = filename_prefix + "_bag_" + my_reader->video_name + "_" + feature_code_string ;

	cout << temp << endl;

	{//read the main file
		string filename = temp + "_" + "main.txt" ;

		cout << filename << endl;

		FILE * fin = fopen( filename.c_str() ,"r");
		fscanf(fin,"%d", &n_classifier);
		//n_classifier = 2;
		fclose( fin );
	}

	initClassifier();

	for(int i = 0 ; i< n_classifier;i++)
	{
		stringstream ss; ss << i;
		string sub_name = temp + "_" + ss.str();
		h[i]->load( sub_name.c_str() );
	}


}


void LcBagging::predict(Mat & feature, Mat & res, Mat & label, vector< LcValidator > & validator_array)
{

	

	int _rows = feature.rows;
	res = Mat::zeros(_rows, 1, 5);

	validator_array.resize( n_classifier);

	for(int i = 0;i< n_classifier;i++)
	{
		Mat temp_empty = Mat(); Mat temp_res;
		
		h[i]->predict( feature, temp_res, temp_empty );
		
		res = res + temp_res;

		res = res * ( 1.0f/ float( i+1 ));

		LcValidator temp_vali( res, label);

		validator_array[i] = temp_vali;

		res = res * float(i+1);
	}

	res = res * ( 1.0f/ float( n_classifier));
}

LcValidator LcBagging::predict( Mat & feature, Mat & res, Mat & label)
{
	int _rows = feature.rows;
	res = Mat::zeros(_rows, 1, 5);

	for(int i = 0;i< n_classifier;i++)
	{
		Mat temp_empty = Mat(); Mat temp_res;
		
		h[i]->predict( feature, temp_res, temp_empty );
		
		res = res + temp_res;
	}

	res = res * ( 1.0f/ float( n_classifier));

	if( label.rows == feature.rows ) return LcValidator( res, label);
	else return LcValidator();
}

void LcBagging::append( Mat & feat    , Mat & lab,
				 Mat & feat_frm, Mat & lab_frm,
				 int & flag )
{
			

	int _rows = feat_frm.rows;

	{//swapping the matrix
		if(veb) cout << "\t swapping the matrix " << endl;

		lc::swapMatrix( feat_frm,  feat_frm, lab_frm, lab_frm);
	}

	int _n = int( downsample_rate * _rows );
	int left = flag;
	int right = flag + _n;

	if(right > feat.rows ){right = feat.rows; _n = right - left;}

	feat_frm.rowRange(0,_n).copyTo(feat.rowRange(left, right));
	lab_frm.rowRange(0,_n).copyTo(lab.rowRange( left, right));

	flag = right;

}

void LcBagging::computeRate()
{
	my_reader->reset();

	

	{//try to deal with first frame and see the size of descriptors.
		Mat frm, desc;
		my_reader->readNext( frm );		

		my_extractor->work( frm, desc );

		int _rows = desc.rows;
		int _cols = desc.cols; feature_dim = _cols;
		int _frame_n = my_reader->getN();

		downsample_rate = float(instance_limit)/(float(_frame_n) * float(_rows));

		if(downsample_rate>1.0f) downsample_rate = 1.0f;

		if(veb)
		{
			cout << "# of frame " << _frame_n << endl;

			cout << "feature per frame " << _rows << " by " << _cols << endl;

			cout << "down sample rate " << downsample_rate << endl;

			cout << "% of data in training " << float(n_classifier) * float(downsample_rate)*100 << " %" << endl;
		}

		instance_limit = int(downsample_rate * _rows) * _frame_n;
	}

	for(double t = (double)getTickCount();(getTickCount()-t)/getTickFrequency()<2;);

}

void LcBagging::setFeatureCode( const char * n_code_string )
{
	feature_code_string = n_code_string;
	my_extractor = new LcFeatureExtractor;
	my_extractor->set_extractor( n_code_string );
}

void LcBagging::setReader( LcVideoRead * n_reader )
{
	my_reader = n_reader;
}

void LcBagging::setExtractor( LcFeatureExtractor * n_extractor )
{
	my_extractor = n_extractor;
}


LcBagging::LcBagging()
{
	veb = 1;
	n_classifier = 2;
	instance_limit = 5000;
	setClassifier( new LcRandomTrees );

	feature_code_string = "";
	setFeatureCode( "l");
}

void LcBagging::initClassifier()
{
	for(int i = 0 ; i< n_classifier ; i++)
	{
		if(1)
		{
			LcClassifier * temp = classifier_prototype->clone();
			temp->veb = veb;
			h.push_back( temp );
		}
		else
		{
			h.push_back( classifier_prototype);
		}
	}
}
void LcBagging::setClassifier( LcClassifier* n_classifier)
{
	classifier_prototype = n_classifier;
}

void LcBagging::initTrain()
{
	if(veb) cout << "start to training " << n_classifier << " bags set in" << endl;
	initClassifier();

	if(veb) cout << "confirm the feature size " << endl;
	computeRate();

}


void LcBagging::predict( LcVideoRead & test_reader , vector< LcValidator > & validator_array)
{
	if(veb)
	{
		cout << " n_classifier is " << n_classifier << endl;
		cout << "train on " << my_reader->video_name << endl;
		cout << "test on "<< test_reader.video_name << endl;
		cout << "[ " << test_reader.f_start << " : " <<
			test_reader.f_rate << " : " <<
			test_reader.f_end << " ]" << endl;
	}

	test_reader.reset();

	validator_array.resize( n_classifier);

	for(int i = 0 ; i< n_classifier;i++) validator_array[i] = LcValidator();

	for(int j = 0 ; j < 1e4; j++)
	{
		Mat frm, desc, gt, lab;

		int statu = test_reader.readNext(frm);

		if(statu!=0) break;

		test_reader.getLabel( gt );

		int frame_id = test_reader.getf();

		if(veb) cout << "==========test on frame " <<
				frame_id << " ==============" << endl;

		my_extractor->work( frm, desc , gt, lab);

		vector< LcValidator > temp_vali_array;

		Mat res;

		predict( desc, res, lab, temp_vali_array);

		for(int i = 0 ; i< n_classifier;i++) validator_array[i] = validator_array[i] + temp_vali_array[i];
	}

}


LcValidator LcBagging::predict( LcVideoRead & test_reader )
{
	test_reader.reset();

	LcValidator ans(0,0,0,0);
	for(int j = 0 ; j < 1e4; j++)
	{
		Mat frm, desc, gt, lab;

		int statu = my_reader->readNext(frm);

		if(statu!=0) break;

		my_reader->getLabel( gt );

		int frame_id = my_reader->getf();

		if(veb) cout << "==========test on frame " <<
				frame_id << " ==============" << endl;

		my_extractor->work( frm, desc , gt, lab);

		Mat res;
		ans = ans + predict( desc, res, lab);
	}

	return ans;
}

void LcBagging::train(LcVideoRead & reader)
{
	setReader( &reader);

	if(veb)
	{
		cout << "video name " << reader.video_name << endl;
		cout << " [ " << reader.f_start << " : " <<
			reader.f_rate << " : " << reader.f_end << " ]" << endl;
	}

	initTrain();

	vector< Mat > descs;
	vector< Mat > labs;

	descs.resize( n_classifier);
	labs.resize( n_classifier );

	
	for(int i = 0; i< n_classifier; i++)
	{

		//int n_data_instance_now = 0;

		Mat & desc = descs[i];
		Mat & lab = labs[i];

		desc = Mat::zeros( instance_limit, feature_dim, 5);
		lab = Mat::zeros( instance_limit, 1 , 5);
	}

	vector<int> n_data_instance_nows( n_classifier, 0);


		my_reader->reset();

		for(int j = 0 ; j < 1e4; j++)
		{
			Mat frm, frm_desc, gt, frm_lab;

			int statu = my_reader->readNext(frm);

			if(statu!=0) break;

			int frame_id = my_reader->getf();

			if(veb) cout << "==========work on frame " <<
				frame_id << " ==============" << endl;

			my_reader->getLabel( gt );

			my_extractor->work( frm, frm_desc , gt, frm_lab);

			for(int i = 0; i< n_classifier; i++)
			{
				Mat & desc = descs[i];
				Mat & lab = labs[i];

				append( desc, lab, frm_desc, frm_lab, n_data_instance_nows[i]);
			}

			frm_desc.release();
			frm_lab.release();
			frm.release();
			gt.release();
		}

			
	for(int i = 0; i< n_classifier; i++)
	{
		Mat & desc = descs[i];
		Mat & lab = labs[i];

		if(n_data_instance_nows[i]<instance_limit)
		{
			cout << " warning! n_data_instance_now " << n_data_instance_nows[i] <<
				" != instance_limit" << instance_limit << " , fix that " << endl;

			desc = desc.rowRange(0,n_data_instance_nows[i]);
			lab = lab.rowRange(0,n_data_instance_nows[i]);
		}

		h[i]->train( desc, lab);

		h[i]->save("temp");

		h[i]->release();

		h[i]->load("temp");

		desc.release();
		lab.release();
	}
}


